/**
 * @file me_order_book.cpp
 * @brief 匹配引擎订单簿的实现
 *
 * 本文件实现了匹配引擎订单簿(MEOrderBook)类的各个方法，包括订单的添加、取消、匹配等核心功能。
 * 订单簿是交易系统的核心组件，负责维护订单并执行撮合，实现价格优先、时间优先的撮合算法。
 *
 * @author 原作者
 * @date 未知
 */

#include "me_order_book.h"

#include "matcher/matching_engine.h"

namespace Exchange {
  /**
   * @brief 订单簿构造函数的实现
   * 
   * 初始化订单簿的各个成员变量，包括交易品种ID、匹配引擎指针、内存池等。
   * 内存池的大小由ME_MAX_PRICE_LEVELS和ME_MAX_ORDER_IDS常量决定，分别用于价格水平对象和订单对象的内存管理。
   * 
   * @param ticker_id 交易品种ID，标识该订单簿所管理的交易品种
   * @param logger 日志记录器，用于记录订单簿操作和状态
   * @param matching_engine 所属的匹配引擎实例，用于发送市场数据和客户端响应
   */
  MEOrderBook::MEOrderBook(TickerId ticker_id, Logger *logger, MatchingEngine *matching_engine)
      : ticker_id_(ticker_id), matching_engine_(matching_engine), orders_at_price_pool_(ME_MAX_PRICE_LEVELS), order_pool_(ME_MAX_ORDER_IDS),
        logger_(logger) {
  }

  /**
   * @brief 订单簿析构函数的实现
   * 
   * 在销毁订单簿对象前，记录当前订单簿状态到日志，并清理资源。
   * 记录的内容包括订单簿的字符串表示，不包含详细信息但执行有效性检查。
   * 然后将指针成员变量设置为nullptr，并清空客户端订单哈希映射。
   */
  MEOrderBook::~MEOrderBook() {
    logger_->log("%:% %() % OrderBook\n%\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str_),
                toString(false, true));

    matching_engine_ = nullptr;
    bids_by_price_ = asks_by_price_ = nullptr;
    for (auto &itr: cid_oid_to_order_) {
      itr.fill(nullptr);
    }
  }

  /**
   * @brief 匹配主动订单与被动订单，生成成交响应和市场更新
   * 
   * 将新的主动订单与指定的被动订单进行匹配，计算成交数量，更新订单状态，
   * 并发送相应的客户端响应和市场数据更新。
   * 
   * @param ticker_id 交易品种ID，标识订单所交易的品种
   * @param client_id 主动订单的客户端ID
   * @param side 主动订单的交易方向
   * @param client_order_id 主动订单的客户端订单ID
   * @param new_market_order_id 主动订单的市场订单ID
   * @param itr 被动订单指针，指向要与主动订单匹配的被动订单
   * @param leaves_qty 指向主动订单剩余数量的指针，匹配后会更新该值
   */
  auto MEOrderBook::match(TickerId ticker_id, ClientId client_id, Side side, OrderId client_order_id, OrderId new_market_order_id, MEOrder* itr, Qty* leaves_qty) noexcept {
    // 保存被动订单指针和原始数量
    const auto order = itr;
    const auto order_qty = order->qty_;
    // 计算成交数量，取主动订单和被动订单数量的最小值
    const auto fill_qty = std::min(*leaves_qty, order_qty);

    // 更新主动订单和被动订单的剩余数量
    *leaves_qty -= fill_qty;
    order->qty_ -= fill_qty;

    // 创建并发送主动订单的成交响应
    client_response_ = {ClientResponseType::FILLED, client_id, ticker_id, client_order_id,
                        new_market_order_id, side, itr->price_, fill_qty, *leaves_qty};
    matching_engine_->sendClientResponse(&client_response_);

    // 创建并发送被动订单的成交响应
    client_response_ = {ClientResponseType::FILLED, order->client_id_, ticker_id, order->client_order_id_,
                        order->market_order_id_, order->side_, itr->price_, fill_qty, order->qty_};
    matching_engine_->sendClientResponse(&client_response_);

    // 创建并发送成交的市场数据更新
    market_update_ = {MarketUpdateType::TRADE, OrderId_INVALID, ticker_id, side, itr->price_, fill_qty, Priority_INVALID};
    matching_engine_->sendMarketUpdate(&market_update_);

    // 如果被动订单完全成交，则从订单簿中移除
    if (!order->qty_) {
      // 创建并发送被动订单取消的市场数据更新
      market_update_ = {MarketUpdateType::CANCEL, order->market_order_id_, ticker_id, order->side_,
                        order->price_, order_qty, Priority_INVALID};
      matching_engine_->sendMarketUpdate(&market_update_);

      // 移除被动订单，并记录性能指标
      START_MEASURE(Exchange_MEOrderBook_removeOrder);
      removeOrder(order);
      END_MEASURE(Exchange_MEOrderBook_removeOrder, (*logger_));
    } else {
      // 如果被动订单部分成交，发送订单修改的市场数据更新
      market_update_ = {MarketUpdateType::MODIFY, order->market_order_id_, ticker_id, order->side_,
                        order->price_, order->qty_, order->priority_};
      matching_engine_->sendMarketUpdate(&market_update_);
    }
  }

  /**
   * @brief 检查新订单是否与现有被动订单匹配
   * 
   * 检查具有指定属性的新订单是否与订单簿另一侧的现有被动订单匹配。
   * 实现价格优先原则，对买单检查卖盘，对卖单检查买盘。
   * 
   * @param client_id 客户端ID，标识下单的客户
   * @param client_order_id 客户端订单ID，客户端用于标识订单的唯一标识符
   * @param ticker_id 交易品种ID，标识订单所交易的品种
   * @param side 交易方向，标识订单是买单还是卖单
   * @param price 价格，订单的限价
   * @param qty 数量，订单的原始数量
   * @param new_market_order_id 新的市场订单ID，匹配引擎内部生成的订单ID
   * @return 剩余数量，如果完全成交则返回0
   */
  auto MEOrderBook::checkForMatch(ClientId client_id, OrderId client_order_id, TickerId ticker_id, Side side, Price price, Qty qty, Qty new_market_order_id) noexcept {
    // 初始化剩余数量为原始数量
    auto leaves_qty = qty;

    // 如果是买单，则与卖盘进行匹配
    if (side == Side::BUY) {
      // 循环匹配，直到没有剩余数量或没有可匹配的卖单
      while (leaves_qty && asks_by_price_) {
        // 获取卖盘最低价的订单
        const auto ask_itr = asks_by_price_->first_me_order_;
        // 如果买单价格低于卖盘最低价，则无法匹配，退出循环
        if (LIKELY(price < ask_itr->price_)) {
          break;
        }

        // 执行匹配，并记录性能指标
        START_MEASURE(Exchange_MEOrderBook_match);
        match(ticker_id, client_id, side, client_order_id, new_market_order_id, ask_itr, &leaves_qty);
        END_MEASURE(Exchange_MEOrderBook_match, (*logger_));
      }
    }
    // 如果是卖单，则与买盘进行匹配
    if (side == Side::SELL) {
      // 循环匹配，直到没有剩余数量或没有可匹配的买单
      while (leaves_qty && bids_by_price_) {
        // 获取买盘最高价的订单
        const auto bid_itr = bids_by_price_->first_me_order_;
        // 如果卖单价格高于买盘最高价，则无法匹配，退出循环
        if (LIKELY(price > bid_itr->price_)) {
          break;
        }

        // 执行匹配，并记录性能指标
        START_MEASURE(Exchange_MEOrderBook_match);
        match(ticker_id, client_id, side, client_order_id, new_market_order_id, bid_itr, &leaves_qty);
        END_MEASURE(Exchange_MEOrderBook_match, (*logger_));
      }
    }

    // 返回剩余数量
    return leaves_qty;
  }

  /**
   * @brief 根据提供的属性创建并添加新订单到订单簿
   * 
   * 该方法是订单簿的主要入口点，用于处理新到达的订单。
   * 首先生成市场订单ID并发送订单接受响应，然后检查新订单是否与现有被动订单匹配。
   * 如果有剩余数量，则将其作为被动订单添加到订单簿中。
   * 
   * @param client_id 客户端ID，标识下单的客户
   * @param client_order_id 客户端订单ID，客户端用于标识订单的唯一标识符
   * @param ticker_id 交易品种ID，标识订单所交易的品种
   * @param side 交易方向，标识订单是买单还是卖单
   * @param price 价格，订单的限价
   * @param qty 数量，订单的原始数量
   */
  auto MEOrderBook::add(ClientId client_id, OrderId client_order_id, TickerId ticker_id, Side side, Price price, Qty qty) noexcept -> void {
    // 生成新的市场订单ID
    const auto new_market_order_id = generateNewMarketOrderId();
    // 创建并发送订单接受响应
    client_response_ = {ClientResponseType::ACCEPTED, client_id, ticker_id, client_order_id, new_market_order_id, side, price, 0, qty};
    matching_engine_->sendClientResponse(&client_response_);

    // 检查新订单是否与现有被动订单匹配，并记录性能指标
    START_MEASURE(Exchange_MEOrderBook_checkForMatch);
    const auto leaves_qty = checkForMatch(client_id, client_order_id, ticker_id, side, price, qty, new_market_order_id);
    END_MEASURE(Exchange_MEOrderBook_checkForMatch, (*logger_));

    // 如果订单有剩余数量，则将其添加到订单簿中作为被动订单
    if (LIKELY(leaves_qty)) {
      // 获取该价格的下一个优先级
      const auto priority = getNextPriority(price);

      // 从内存池中分配新订单对象
      auto order = order_pool_.allocate(ticker_id, client_id, client_order_id, new_market_order_id, side, price, leaves_qty, priority, nullptr,
                                         nullptr);
      // 将订单添加到订单簿中，并记录性能指标
      START_MEASURE(Exchange_MEOrderBook_addOrder);
      addOrder(order);
      END_MEASURE(Exchange_MEOrderBook_addOrder, (*logger_));

      // 创建并发送订单添加的市场数据更新
      market_update_ = {MarketUpdateType::ADD, new_market_order_id, ticker_id, side, price, leaves_qty, priority};
      matching_engine_->sendMarketUpdate(&market_update_);
    }
  }

  /**
   * @brief 尝试取消订单簿中的订单
   * 
   * 根据客户端ID和订单ID查找并取消订单。
   * 如果订单不存在或无法取消，则发送取消拒绝响应。
   * 如果订单存在且可以取消，则从订单簿中移除该订单，并发送相应的市场数据更新和客户端响应。
   * 
   * @param client_id 客户端ID，标识下单的客户
   * @param order_id 订单ID，客户端用于标识订单的唯一标识符
   * @param ticker_id 交易品种ID，标识订单所交易的品种
   */
  auto MEOrderBook::cancel(ClientId client_id, OrderId order_id, TickerId ticker_id) noexcept -> void {
    // 检查客户端ID是否有效
    auto is_cancelable = (client_id < cid_oid_to_order_.size());
    MEOrder *exchange_order = nullptr;
    if (LIKELY(is_cancelable)) {
      // 获取客户端的订单映射
      auto &co_itr = cid_oid_to_order_.at(client_id);
      // 获取指定订单ID的订单
      exchange_order = co_itr.at(order_id);
      // 检查订单是否存在
      is_cancelable = (exchange_order != nullptr);
    }

    // 如果订单不存在或无法取消
    if (UNLIKELY(!is_cancelable)) {
      // 创建取消拒绝响应
      client_response_ = {ClientResponseType::CANCEL_REJECTED, client_id, ticker_id, order_id, OrderId_INVALID,
                          Side::INVALID, Price_INVALID, Qty_INVALID, Qty_INVALID};
    } else {
      // 创建取消成功响应
      client_response_ = {ClientResponseType::CANCELED, client_id, ticker_id, order_id, exchange_order->market_order_id_,
                          exchange_order->side_, exchange_order->price_, Qty_INVALID, exchange_order->qty_};
      // 创建取消订单的市场数据更新
      market_update_ = {MarketUpdateType::CANCEL, exchange_order->market_order_id_, ticker_id, exchange_order->side_, exchange_order->price_, 0,
                        exchange_order->priority_};

      // 从订单簿中移除订单，并记录性能指标
      START_MEASURE(Exchange_MEOrderBook_removeOrder);
      removeOrder(exchange_order);
      END_MEASURE(Exchange_MEOrderBook_removeOrder, (*logger_));

      // 发送市场数据更新
      matching_engine_->sendMarketUpdate(&market_update_);
    }

    // 发送客户端响应
    matching_engine_->sendClientResponse(&client_response_);
  }

  /**
   * @brief 将订单簿转换为字符串表示
   * 
   * 生成订单簿的文本表示，包括买卖盘的所有价格水平和订单。
   * 可用于日志记录、调试和状态监控。
   * 
   * @param detailed 是否包含详细信息，如果为true则包含每个订单的详细信息
   * @param validity_check 是否执行有效性检查，验证订单簿的内部一致性
   * @return 订单簿的字符串表示
   */
  auto MEOrderBook::toString(bool detailed, bool validity_check) const -> std::string {
    std::stringstream ss;
    std::string time_str;

    // 定义一个内部函数，用于打印单个价格水平的信息
    auto printer = [&](std::stringstream &ss, MEOrdersAtPrice *itr, Side side, Price &last_price, bool sanity_check) {
      char buf[4096];
      Qty qty = 0;
      size_t num_orders = 0;

      // 计算该价格水平的总数量和订单数
      for (auto o_itr = itr->first_me_order_;; o_itr = o_itr->next_order_) {
        qty += o_itr->qty_;
        ++num_orders;
        if (o_itr->next_order_ == itr->first_me_order_)
          break;
      }
      // 格式化价格水平的基本信息，包括价格、前后节点价格、总数量和订单数
      sprintf(buf, " <px:%3s p:%3s n:%3s> %-3s @ %-5s(%-4s)",
              priceToString(itr->price_).c_str(), priceToString(itr->prev_entry_->price_).c_str(), priceToString(itr->next_entry_->price_).c_str(),
              priceToString(itr->price_).c_str(), qtyToString(qty).c_str(), std::to_string(num_orders).c_str());
      ss << buf;
      // 如果需要详细信息，则打印每个订单的详细信息
      for (auto o_itr = itr->first_me_order_;; o_itr = o_itr->next_order_) {
        if (detailed) {
          sprintf(buf, "[oid:%s q:%s p:%s n:%s] ",
                  orderIdToString(o_itr->market_order_id_).c_str(), qtyToString(o_itr->qty_).c_str(),
                  orderIdToString(o_itr->prev_order_ ? o_itr->prev_order_->market_order_id_ : OrderId_INVALID).c_str(),
                  orderIdToString(o_itr->next_order_ ? o_itr->next_order_->market_order_id_ : OrderId_INVALID).c_str());
          ss << buf;
        }
        if (o_itr->next_order_ == itr->first_me_order_)
          break;
      }

      ss << std::endl;

      // 如果需要进行有效性检查，则验证价格水平的排序是否正确
      if (sanity_check) {
        if ((side == Side::SELL && last_price >= itr->price_) || (side == Side::BUY && last_price <= itr->price_)) {
          FATAL("Bids/Asks not sorted by ascending/descending prices last:" + priceToString(last_price) + " itr:" + itr->toString());
        }
        last_price = itr->price_;
      }
    };

    // 打印交易品种信息
    ss << "Ticker:" << tickerIdToString(ticker_id_) << std::endl;
    // 打印卖盘信息
    {
      auto ask_itr = asks_by_price_;
      auto last_ask_price = std::numeric_limits<Price>::min();
      for (size_t count = 0; ask_itr; ++count) {
        ss << "ASKS L:" << count << " => ";
        auto next_ask_itr = (ask_itr->next_entry_ == asks_by_price_ ? nullptr : ask_itr->next_entry_);
        printer(ss, ask_itr, Side::SELL, last_ask_price, validity_check);
        ask_itr = next_ask_itr;
      }
    }

    // 打印分隔线
    ss << std::endl << "                          X" << std::endl << std::endl;

    // 打印买盘信息
    {
      auto bid_itr = bids_by_price_;
      auto last_bid_price = std::numeric_limits<Price>::max();
      for (size_t count = 0; bid_itr; ++count) {
        ss << "BIDS L:" << count << " => ";
        auto next_bid_itr = (bid_itr->next_entry_ == bids_by_price_ ? nullptr : bid_itr->next_entry_);
        printer(ss, bid_itr, Side::BUY, last_bid_price, validity_check);
        bid_itr = next_bid_itr;
      }
    }

    return ss.str();
  }
}
